using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web;
using System.Data;
using Web.Ajax.Controls;
using Web.Ajax.Configuration;
using Action = Web.Ajax.Controls.Action;

namespace Web.Ajax.Logging
{
    public class Log 
    {
        public DateTime TimeStamp=DateTime.Now;
        public Level Level;
        public string Id=Guid.NewGuid().ToString();
        public string RequestId;
        public string User;
        public string Message;
		public string Data;
        public int RequestOrder;
        public LogException Exception;
        private LogEndpoint LogEndpoint = Configuration.Logs.Current.GetEndpoint();


        public Log()
        {
        }

        public Log(Level level, string message, Exception e)
        {
            Level = level;
            if (string.IsNullOrEmpty(message) && e != null)
                Message = e.Message;
            else
                Message = message;
            Exception = LogException.Create(e);
            RequestId = LogEndpoint.CurrentRequestId;
            if (HttpContext.Current != null)
            {
                if (HttpContext.Current.User != null && HttpContext.Current.User.Identity != null)
                    User = HttpContext.Current.User.Identity.Name;
            }
        }

		public static string Debug(string message, Exception e)
        {
            return WriteLog(Level.Debug, message, e);
        }
        public static string Debug(string message)
        {
            return Debug(message, null);
        }
        public static string Debug(Exception e)
        {
            return Debug(null, e);
        }


		public static string Info(string message, Exception e, string data)
		{
			return WriteLog(Level.Info, message, e, data);
		}
		public static string Info(string message, Exception e)
        {
			return Info(message, e, null);
        }
        public static string Info(string message)
        {
            return Info(message, null);
        }
        public static string Info(Exception e)
        {
            return Info(null, e);
        }    


        public static string Warn(string message, Exception e)
        {
            return WriteLog(Level.Warn,message,e);
        }
        public static string Warn(string message)
        {
            return Warn(message, null);
        }
        public static string Warn(Exception e)
        {
            return Warn(null, e);
        }


        public static string Error(string message, Exception e)
        {
            return WriteLog(Level.Error, message, e);
        }
        public static string Error(string message)
        {
            return Error(message, null);
        }
        public static string Error(Exception e)
        {
            return Error(null, e);
        }

        public static string Fatal(string message, Exception e)
        {
            return WriteLog(Level.Fatal, message, e);
        }
        public static string Fatal(string message)
        {
            return Fatal(message, null);
        }
        public static string Fatal(Exception e)
        {
            return Fatal(null, e);
        }

        [Json(Ignore = true)]
        public static bool IsDebugEnabled
        {
            get { return true; }
        }
        [Json(Ignore = true)]
        public static bool IsInfoEnabled
        {
            get { return true; }
        }
        [Json(Ignore = true)]
        public static bool IsWarnEnabled
        {
            get { return true; }
        }
        [Json(Ignore = true)]
        public static bool IsErrorEnabled
        {
            get {   return true; }
        }
        [Json(Ignore = true)]
        public static bool IsFatalEnabled
        {
            get { return true; }
        }               
        
      
        private static readonly object PadLock=new object();
        private static void WriteLog(Log l)
        {
            if (l == null)
                return;
            if (l.Level < l.LogEndpoint.Level)
                return;
            try
            {
                lock (PadLock)
                {
                    bool firstwrite = false;
                    string LogFilePath = l.LogEndpoint.LogFilePath;
                    if (!File.Exists(LogFilePath))
                        firstwrite = true;
                    else
                    {
                        using (StreamReader reader = new StreamReader(LogFilePath))
                        {
                            string s = reader.ReadToEnd();
                            if (s.Length == 0)
                                firstwrite = true;
                        }
                    }
                    using (StreamWriter writer = new StreamWriter(LogFilePath, true))
                    {
                        int order = 1;
                        if (HttpContext.Current != null)
                        {
                            object o=HttpContext.Current.Items["LogRequestOrder"];
                            if (o is int)
                            {
                                order = (int)o;
                            }
                            HttpContext.Current.Items["LogRequestOrder"] = order + 1;
                        }
                        l.RequestOrder=order;
                        string json = Json.ConvertToJson(l, false);
                        if (!firstwrite)
                        {
                            writer.Write(",");
                            writer.Write("\r\n");
                        }
                        writer.Write(json);
                    }
                }
            }
            catch (Exception)
            {
            }
        }
        private static string WriteLog(Level level, string message, Exception e, string data)
        {
            Log l = new Log(level, message, e);
			l.Data = data;
            WriteLog(l);
            return l.Id;
        }
		private static string WriteLog(Level level, string message, Exception e)
		{
			return WriteLog(level, message, e, null);
		}



        /// <summary>
        /// Clears the Logfile passed.
        /// </summary>
        public static void Clear(string path)
        {
            try
            {
                lock (PadLock)
                {
                    using (StreamWriter writer = new StreamWriter(path, false))
                    {
                        writer.Write("");
                    }
                }
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Deletes the Logfile passed. Absorbs exceptions.
        /// </summary>
        /// <param name="path"></param>
        public static void Delete(string path)
        {
            try
            {
                lock (PadLock)
                {
                    File.Delete(path);
                }
            }
            catch (Exception)
            {
            }
        }

        public static void Delete(TreeData t)
        {
            if (t == null)
                return;
            if (!string.IsNullOrEmpty(t.Id)&&t.Selected)
                Delete(t.Id);
            if (t.Children != null)
            {
                for (int i = 0; i < t.Children.Length; i++)
                    Delete(t.Children[i]);
            }
        }

        public static Log[] GetLogs(string logfilepath)
        {
            if (logfilepath == null)
            {
                LogEndpoint e = Configuration.Logs.Current.GetEndpoint();
                logfilepath = e.LogFilePath;
            }
            string json;
            if (!File.Exists(logfilepath))
                throw new Exception("Log File does not exist: "+logfilepath);
            using (StreamReader reader = new StreamReader(logfilepath))
            {
                json = reader.ReadToEnd();
            }            
            return (Log[])Json.ConvertFromJson(typeof(Log[]),"["+json+"]");
        }

        public static string ImageHtml(Level l)
        {
            string resource=null;
            switch (l)
            {
                case Level.Error:
                case Level.Fatal:
                    resource = Resources.ImageUrl(Resources.Images.Failure);
                    break;
                case Level.Info:
                    resource = Resources.ImageUrl(Resources.Images.Info);
                    break;
                case Level.Warn:
                    resource = Resources.ImageUrl(Resources.Images.Warn);
                    break;
                default:
                    break;
            }

            if (string.IsNullOrEmpty(resource))
                return "";
            string html = "<img src=\""+resource+"\" title=\""+l.ToString()+"\" />";
            return html;
        }

        public static TreeData[] GetFileTreeDataForFolder(string Folder, bool AddViewParent, string searchtext)
        {
            List<TreeData> tree = new List<TreeData>();
            DirectoryInfo RootDir = new DirectoryInfo(Folder);
            
            if (AddViewParent)
            {
                TreeData t = new TreeData();
                t.Resource = Resources.GetClientName(Resources.Images.ViewParent);
                t.Title = "Parent Folder";
                t.Id = RootDir.Parent.FullName;
                t.OnTitleClick = "SelectFolder(string);";
                tree.Add(t);
            }

            
            string filter = "*" + LogEndpoint.LogFileExtension;

            DirectoryInfo[] dirs = RootDir.GetDirectories();

            for (int i = 0; i < dirs.Length; i++)
            {
                DirectoryInfo d = dirs[i];
                TreeData t = new TreeData();
                t.Resource = Resources.GetClientName(Resources.Images.Folder);
                t.Title = d.Name;
                t.Id = d.FullName;
                t.Collapse = true;
                t.OnTitleClick = "SelectFolder(string);";
                t.Ajax = true;
                //t.Children = GetFileTreeDataForFolder(d.FullName, false);
                //if(t.Children.Length>0)
                tree.Add(t);
            }

            FileInfo[] RootFiles = RootDir.GetFiles(filter);
            for (int i = 0; i < RootFiles.Length; i++)
            {
                FileInfo f = RootFiles[i];
                TreeData child = new TreeData();
                child.Resource = Resources.GetClientName(Resources.Images.LogFile);
                child.Title = f.Name.Replace(LogEndpoint.LogFileExtension, "");
                child.Id = f.FullName;
                child.OnTitleClick = "SelectLogFile(string);";
                child.Max = 0;
                child.Selectable = true;

				var add=true;
				if (!string.IsNullOrEmpty(searchtext))
				{
					add=false;
					var logs = GetLogs(f.FullName);
					foreach (var l in logs)
					{
						if (l.Message!=null&&l.Message.IndexOf(searchtext) > -1)
						{
							add = true;
							break;
						}
						if (l.Data != null && l.Data.IndexOf(searchtext) > -1)
						{
							add = true;
							break;
						}
						if (searchtext.IndexOf("errors") > -1 && (int)l.Level >= (int)Level.Error)
						{
							add = true;
							break;
						}
					}
					if (add == false && f.Name.IndexOf(searchtext) > -1)
						add = true;
				}
				if(add)
					tree.Add(child);
            }
            return tree.ToArray();
        }

        public static TreeData[] GetFileTreeData(TreeInfo ti)
        {
            string LogFolder=ti.GetParameter("LogFolder");
            if (ti.Path != null)
                LogFolder = ti.Path;

            bool ViewParent = ti.GetParameter("ViewParent")=="true";

            if (string.IsNullOrEmpty(LogFolder))
                throw new Exception("No LogFolder provided.");


            if (ti.Action == Action.Delete&&ti.Data!=null)
            {
                for(int i=0;i<ti.Data.Length;i++)
                    Delete(ti.Data[i]);
            }

			var searchtext = ti.GetParameter("Search");
			if (string.IsNullOrEmpty(searchtext))
				searchtext = null;
            return GetFileTreeDataForFolder(LogFolder,ViewParent, searchtext);
        }

        public static DataTable GetLogDataTable(GridInfo gi)
        {
            string LogFile=gi.GetParameter("LogFile");
            int? level = gi.GetIntParameter("Level");
            
            Log[] logs = GetLogs(LogFile);
            DataTable dt = new DataTable();
            dt.Columns.Add("Id", typeof(string));
            dt.Columns.Add("Image", typeof(string));
            dt.Columns.Add("Time", typeof(DateTime));
            dt.Columns.Add("Level",typeof(string));
            dt.Columns.Add("Message",typeof(string));
            dt.Columns.Add("ExceptionOption",typeof(Options));
            dt.Columns.Add("Exception", typeof(string));
            dt.Columns.Add("RequestId", typeof(string));
            dt.Columns.Add("User", typeof(string));
            dt.Columns.Add("Order", typeof(int));
			dt.Columns.Add("Data", typeof(string));
            
            for (int i = 0; i < logs.Length; i++)
            {
                Log l = logs[i];
                try
                {
                    if (level.HasValue && ((int)l.Level) < level.Value)
                        continue;
                    dt.Rows.Add(
                        l.Id,
                        ImageHtml(l.Level),
                        l.TimeStamp,
                        l.Level.ToString(),
                        l.Message,
                        new Options("ShowStack(this,'" + l.Id + "');"),
                        Json.ConvertToJson(l.Exception),
                        l.RequestId,
                        l.User,
                        l.RequestOrder,
						l.Data
                        );
                }
                catch (Exception)
                {
                }

            }
            return dt;
        }
    }
    /*
    public interface ILog
    {
        string Debug(string message, Exception e);
        string Debug(string message);
        string Debug(Exception e);
        string Info(string message, Exception e);
        string Info(string message);
        string Info(Exception e);
        string Warn(string message, Exception e);
        string Warn(string message);
        string Warn(Exception e);
        string Error(string message, Exception e);
        string Error(string message);
        string Error(Exception e);
        string Fatal(string message, Exception e);
        string Fatal(string message);
        string Fatal(Exception e);

        bool IsDebugEnabled{get;}
        bool IsInfoEnabled { get;}
        bool IsWarnEnabled { get;}
        bool IsErrorEnabled { get;}
        bool IsFatalEnabled { get;}
    }*/

    public class LogException
    {
        public string Message;
        public string StackTrace;
        public LogException InnerException;

        public static LogException Create(Exception e)
        {
            if (e == null)
                return null;
            LogException ret = new LogException();
            ret.Message = e.Message;
            ret.StackTrace = e.StackTrace;
            ret.InnerException = Create(e.InnerException);
            return ret;
        }
    }    
}
